﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Threading;
//using System.Timers;
using System.Windows.Forms;

namespace UserInterface
{
    [Serializable()]
    public class CrossingType2 : Crossing
    {

        /* 
         * And a function that adds a pedestrian on that list
         * And also changes the sensor of the TLPedestrians to <b>true<b>
        */
        bool first = true;

        int intervalRunning = 0;
        int intervalToRun;

        int indexGreen;
        int indexRed;

        private List<Pedestrian> myPedestrians;

        private LinkedList<PedLane>[] pedestrianLanes;

        delegate void myDelegate(System.Timers.Timer t);

        Random rdm = new Random();
        
        [NonSerialized]
        System.Windows.Forms.Timer timer;

        //constructors
        /// <summary>
        /// This is the constructor of Crossing of type 2.
        /// </summary>
        /// <param name="p">This is the position of the crossing on the map.</param>
        /// <param name="index">This is the position of the crossing in the list (myCrossings.Count)</param>
        public CrossingType2(Point p, int i, PictureBox pbx)//adding PictureBox pb in the argument
        {
            Random rmd = new Random();
            indexGreen = rmd.Next(0, 4);
            
            Pos = p;
            im = Rsrc_Images.Type2_SMALL;
            crosType = Parent.Type2;

            pbox = pbx;

            run =  false;

            index = i;
                        
            timer = new System.Windows.Forms.Timer();
            timer.Tick += new EventHandler(timer_Tick);
            timer.Interval = 70;

            intervalToRun = 4000;

            myPedestrians = new List<Pedestrian>();

            blackboxLanes = new List<Lane>();
            outgoingLanes = new List<Lane>();
                   
            myIncomingLanes = new LinkedList<Lane>[4];
            myIncomingLanes[0] = new LinkedList<Lane>();
            myIncomingLanes[1] = new LinkedList<Lane>();
            myIncomingLanes[2] = new LinkedList<Lane>();
            myIncomingLanes[3] = new LinkedList<Lane>();

            myIncomingLanes[0].AddLast(new Lane("N1", new Point(125, 0), new Point(125, 83), Direction.S, Parent.Type2, Rsrc_Images.car_20x20_S));
            myIncomingLanes[1].AddLast(new Lane("S2", new Point(180, 333), new Point(180, 233), Direction.N, Parent.Type2, Rsrc_Images.car_20x20_N));
            myIncomingLanes[2].AddLast(new Lane("W2", new Point(0, 165), new Point(75, 165), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_E));
            myIncomingLanes[2].AddLast(new Lane("W3", new Point(0, 210), new Point(75, 210), Direction.SE, Parent.Type2, Rsrc_Images.car_20x20_E));
            myIncomingLanes[3].AddLast(new Lane("E1", new Point(303, 125), new Point(210, 125), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_W));
            myIncomingLanes[3].AddLast(new Lane("E2", new Point(303, 175), new Point(210, 175), Direction.WS, Parent.Type2, Rsrc_Images.car_20x20_W));

            blackboxLanes.Add(new Lane("N1-E3", new Point(125, 88), new Point(210, 210), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_WS));
            blackboxLanes.Add(new Lane("N1-S1", new Point(125, 88), new Point(125, 213), Direction.SW, Parent.Type2, Rsrc_Images.car_20x20_S));
            blackboxLanes.Add(new Lane("N1-W1", new Point(125, 88), new Point(95, 115), Direction.WN, Parent.Type2, Rsrc_Images.car_20x20_ES));
            blackboxLanes.Add(new Lane("E1-N3", new Point(215, 125), new Point(180, 103), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_SW));
            blackboxLanes.Add(new Lane("E1-W1", new Point(215, 125), new Point(115, 115), Direction.WN, Parent.Type2, Rsrc_Images.car_20x20_W));
            blackboxLanes.Add(new Lane("E2-S1", new Point(215, 175), new Point(125, 233), Direction.SW, Parent.Type2, Rsrc_Images.car_20x20_ES));
            blackboxLanes.Add(new Lane("S2-W1", new Point(175, 235), new Point(95, 125), Direction.WN, Parent.Type2, Rsrc_Images.car_20x20_SW));
            blackboxLanes.Add(new Lane("S2-N3", new Point(180, 233), new Point(180, 103), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_N));
            blackboxLanes.Add(new Lane("S2-E3", new Point(185, 233), new Point(210, 210), Direction.ES, Parent.Type2, Rsrc_Images.car_20x20_SE));
            blackboxLanes.Add(new Lane("W2-N3", new Point(75, 160), new Point(180, 103), Direction.NE, Parent.Type2, Rsrc_Images.car_20x20_SE));
            blackboxLanes.Add(new Lane("W3-E3", new Point(75, 210), new Point(210, 210), Direction.ES, Parent.Type2, Rsrc_Images.car_20x20_E));
            blackboxLanes.Add(new Lane("W3-S1", new Point(75, 215), new Point(125, 213), Direction.SW, Parent.Type2, Rsrc_Images.car_20x20_WS));

            outgoingLanes.Add(new Lane("N3", new Point(180, 83), new Point(180, 0), Direction.S, Parent.Type2, Rsrc_Images.car_20x20_N));
            outgoingLanes.Add(new Lane("S1", new Point(125, 233), new Point(125, 333), Direction.N, Parent.Type2, Rsrc_Images.car_20x20_S));
            outgoingLanes.Add(new Lane("E3", new Point(230, 210), new Point(303, 210), Direction.W, Parent.Type2, Rsrc_Images.car_20x20_E));
            outgoingLanes.Add(new Lane("W1", new Point(95, 115), new Point(0, 115), Direction.E, Parent.Type2, Rsrc_Images.car_20x20_W));

            myTrafficLight = new LinkedList<TrafficLight>[4];

            myTrafficLight[0] = new LinkedList<TrafficLight>();
            myTrafficLight[1] = new LinkedList<TrafficLight>();
            myTrafficLight[2] = new LinkedList<TrafficLight>();
            myTrafficLight[3] = new LinkedList<TrafficLight>();
            //myTrafficLight[4] = new LinkedList<TrafficLight>();
            //myTrafficLight[5] = new LinkedList<TrafficLight>();

            myTrafficLight[0].AddLast(new TrafficLight("N1", new Point(85, 96), Color.Red));
            myTrafficLight[1].AddLast(new TrafficLight("S2", new Point(212, 232), Color.Red));
            myTrafficLight[2].AddLast(new TrafficLight("W2", new Point(88, 138), Color.Red));
            myTrafficLight[2].AddLast(new TrafficLight("W3", new Point(80, 234), Color.Red));
            myTrafficLight[2].AddLast(new TLPedestrian("TPSLeft", new Point(88, 305), Color.Red));
            myTrafficLight[2].AddLast(new TLPedestrian("TPSRight", new Point(212, 305), Color.Red));
            myTrafficLight[3].AddLast(new TrafficLight("E1", new Point(205, 95), Color.Red));
            myTrafficLight[3].AddLast(new TrafficLight("E2", new Point(205, 190), Color.Red));
            myTrafficLight[3].AddLast(new TLPedestrian("TPNLeft", new Point(80, 20), Color.Red));
            myTrafficLight[3].AddLast(new TLPedestrian("TPNRight", new Point(212, 20), Color.Red));

            //myTrafficLight[4].AddLast(new TLPedestrian("TPSLeft", new Point(88, 305), Color.Red));
            //myTrafficLight[4].AddLast(new TLPedestrian("TPSRight", new Point(212, 305), Color.Red));
            //myTrafficLight[5].AddLast(new TLPedestrian("TPNLeft", new Point(80, 20), Color.Red));
            //myTrafficLight[5].AddLast(new TLPedestrian("TPNRight", new Point(212, 20), Color.Red));

            for (int j = 0; j < myIncomingLanes.Length; j++)
            {
                foreach (Lane l in myIncomingLanes[j])
                {
                    l.flux = Flux.ingress;
                }
            }
            foreach (Lane l in outgoingLanes)
            { l.flux = Flux.egress; l.canMove = true; }
            foreach (Lane l in blackboxLanes)
            { l.flux = Flux.InOut; l.canMove = true; }

            pedestrianLanes = new LinkedList<PedLane>[2];
            pedestrianLanes[0] = new LinkedList<PedLane>();
            pedestrianLanes[1] = new LinkedList<PedLane>();

            int pedSize = 15;

            pedestrianLanes[0].AddLast(new PedLane("NLeft", new Point(40, 30), new Point(90 - pedSize, 30), Direction.E));
            pedestrianLanes[0].AddLast(new PedLane("NLeft-Right1", new Point(90 + pedSize, 30), new Point(212 - pedSize, 30), Direction.E));
            pedestrianLanes[0].AddLast(new PedLane("NLeftExit", new Point(212 + pedSize, 30), new Point(260, 30), Direction.E));
            pedestrianLanes[0].AddLast(new PedLane("NRight", new Point(260, 45), new Point(212, 45), Direction.W));
            pedestrianLanes[0].AddLast(new PedLane("NRight-Left1", new Point(212-pedSize, 45), new Point(90, 45), Direction.W));
            pedestrianLanes[0].AddLast(new PedLane("NRightExit", new Point(90-pedSize, 45), new Point(40, 45), Direction.W));

            pedestrianLanes[1].AddLast(new PedLane("SLeft", new Point(40, 280), new Point(90 - pedSize, 280), Direction.E));
            pedestrianLanes[1].AddLast(new PedLane("SLeft-Right1", new Point(90 + pedSize, 280), new Point(212 - pedSize, 280), Direction.E ));
            pedestrianLanes[1].AddLast(new PedLane("SLeftExit", new Point(212 + pedSize, 280), new Point(260, 280), Direction.E));
            pedestrianLanes[1].AddLast(new PedLane("SRight", new Point(260, 295), new Point(212, 295), Direction.W));
            pedestrianLanes[1].AddLast(new PedLane("SRight-Left1", new Point(212 - pedSize, 295), new Point(90, 295), Direction.W));
            pedestrianLanes[1].AddLast(new PedLane("SRightExit", new Point(90 - pedSize, 295), new Point(40, 295), Direction.W));

            this.setDestinationsForLanes();
            
            myIncomingLanes[0].ElementAt(0).pl = new List<PedLane> { pedestrianLanes[0].ElementAt(1), pedestrianLanes[0].ElementAt(4) };
            myIncomingLanes[1].ElementAt(0).pl = new List<PedLane> { pedestrianLanes[1].ElementAt(1), pedestrianLanes[1].ElementAt(4) };

            outgoingLanes[0].pl = new List<PedLane> { pedestrianLanes[0].ElementAt(1), pedestrianLanes[0].ElementAt(4) };
            outgoingLanes[1].pl = new List<PedLane> { pedestrianLanes[1].ElementAt(1), pedestrianLanes[1].ElementAt(4) };

           // outgoingLanes[0].pl = pedestrianLanes[0].ElementAt(2);

            pbox.Paint += new PaintEventHandler(this.getallCars2Draw);
        }
        
        ~CrossingType2()
        {
            myTrafficLight = null;
            pbox.Paint -= new PaintEventHandler(getallCars2Draw);
        }


        //methods
        bool changed2orange = false;
        private void timer_Tick(object sender, EventArgs e)
        {            
            intervalRunning += timer.Interval;

            if (intervalRunning >= intervalToRun - 1500 && !changed2orange)
            { changeToOrange(indexGreen); }

            if (intervalRunning >= intervalToRun)
            {
                intervalRunning = 0;
                indexGreen = (indexGreen + 1) % 4;
                if (indexGreen == 0)
                {
                    indexRed = 3;
                }
                else
                {
                    indexRed = indexGreen - 1;
                }
                ChangeColors(indexGreen, indexRed);

                
            }
            RunMyPattern();
            pbox.Refresh();
        }

        /// <summary>
        /// 
        /// </summary>
        private void setDestinationsForLanes()
        {
            myIncomingLanes[0].ElementAt(0).addDestination(blackboxLanes[getIndexLane("N1-S1")]);
            myIncomingLanes[0].ElementAt(0).addDestination(blackboxLanes[getIndexLane("N1-W1")]);
            myIncomingLanes[0].ElementAt(0).addDestination(blackboxLanes[getIndexLane("N1-E3")]);

            myIncomingLanes[1].ElementAt(0).addDestination(blackboxLanes[getIndexLane("S2-W1")]);
            myIncomingLanes[1].ElementAt(0).addDestination(blackboxLanes[getIndexLane("S2-N3")]);
            myIncomingLanes[1].ElementAt(0).addDestination(blackboxLanes[getIndexLane("S2-E3")]);

            myIncomingLanes[2].ElementAt(0).addDestination(blackboxLanes[getIndexLane("W2-N3")]);
            myIncomingLanes[2].ElementAt(1).addDestination(blackboxLanes[getIndexLane("W3-E3")]);
            myIncomingLanes[2].ElementAt(1).addDestination(blackboxLanes[getIndexLane("W3-S1")]);

            myIncomingLanes[3].ElementAt(0).addDestination(blackboxLanes[getIndexLane("E1-W1")]);
            myIncomingLanes[3].ElementAt(0).addDestination(blackboxLanes[getIndexLane("E1-N3")]);
            myIncomingLanes[3].ElementAt(1).addDestination(blackboxLanes[getIndexLane("E2-S1")]);

            for (int i = 0; i < blackboxLanes.Count; i++ )
            {
                string name = blackboxLanes[i].LaneId.Substring(3, 2);

                blackboxLanes[i].addDestination(outgoingLanes[getIndexLane(name)]);
            }

            pedestrianLanes[0].ElementAt(0).addDestination(pedestrianLanes[0].ElementAt(1));
            pedestrianLanes[0].ElementAt(1).addDestination(pedestrianLanes[0].ElementAt(2));
            pedestrianLanes[0].ElementAt(2).addDestination(null);
            pedestrianLanes[0].ElementAt(3).addDestination(pedestrianLanes[0].ElementAt(4));
            pedestrianLanes[0].ElementAt(4).addDestination(pedestrianLanes[0].ElementAt(5));
            pedestrianLanes[0].ElementAt(5).addDestination(null);


            pedestrianLanes[1].ElementAt(0).addDestination(pedestrianLanes[1].ElementAt(1));
            pedestrianLanes[1].ElementAt(1).addDestination(pedestrianLanes[1].ElementAt(2));
            pedestrianLanes[1].ElementAt(2).addDestination(null);
            pedestrianLanes[1].ElementAt(3).addDestination(pedestrianLanes[1].ElementAt(4));
			pedestrianLanes[1].ElementAt(4).addDestination(pedestrianLanes[1].ElementAt(5));
            pedestrianLanes[1].ElementAt(5).addDestination(null);
        }

        /// <summary>
        /// It checks the 
        /// </summary>
        /// <param name="lane"></param>
        /// <returns></returns>
        private int getIndexLane(string lane)
        {
            if (lane.Length == 2)
            {
                for (int i = 0; i < outgoingLanes.Count; i++)
                {
                    if (outgoingLanes[i].LaneId == lane)
                    {
                        return i;
                    }
                }
            }
            else
            {
                for (int j = 0; j < blackboxLanes.Count; j++)
                {
                    if (blackboxLanes[j].LaneId == lane)
                    {
                        return j;
                    }
                }
            }
            return -1;

        }
        

        /// <summary>
        /// This function starts running the pattern of the crossing by starting the timer.
        /// </summary>
        public override void RunMyPattern()
        {            
            for (int i = 0; i < 4; i++)
            {
                foreach (Lane il in myIncomingLanes[i])
                {
                   // il.tmr_move.Enabled = true;
                    il.Move(null, null);
                }
            }
            foreach (Lane bl in blackboxLanes)
            {
               // bl.tmr_move.Enabled = true;
                bl.Move(null, null);
            }

            foreach (Lane ol in outgoingLanes)
            {
               // ol.tmr_move.Enabled = true;
                ol.Move(null, null);
            }

            if (indexGreen == 2 || indexGreen == 4)
            {
                pedestrianLanes[1].ElementAt(0).canMove = true;
                pedestrianLanes[1].ElementAt(3).canMove = true;
                pedestrianLanes[0].ElementAt(0).canMove = false;
                pedestrianLanes[0].ElementAt(3).canMove = false;
               
            }
            else if (indexGreen == 3 || indexGreen == 5)
            {
                pedestrianLanes[0].ElementAt(0).canMove = true;
                pedestrianLanes[0].ElementAt(3).canMove = true;
                pedestrianLanes[1].ElementAt(0).canMove = false;
                pedestrianLanes[1].ElementAt(3).canMove = false;
            }
            else 
            {
                pedestrianLanes[0].ElementAt(0).canMove = false;
                pedestrianLanes[0].ElementAt(3).canMove = false;
                pedestrianLanes[1].ElementAt(0).canMove = false;
                pedestrianLanes[1].ElementAt(3).canMove = false;
            }

            foreach (PedLane pl in pedestrianLanes[0])
                pl.Move(null, null);

            foreach (PedLane pl in pedestrianLanes[1])
                pl.Move(null, null);               
            

            if (first)
            {
                if (indexGreen == 0)
                {
                    indexRed = 3;
                }
                else
                {
                    indexRed = indexGreen - 1;
                }
                ChangeColors(indexGreen, indexRed);
                first = false;

            }
                // When you load the class from a file the timer is null
                if (timer == null)
                {
                    timer = new System.Windows.Forms.Timer();
                    timer.Tick += new EventHandler(timer_Tick);
                    timer.Interval = 70;
                }
            if (!timer.Enabled)
                    timer.Start();

            this.pbox.Invalidate();
        }

        /// <summary>
        /// This function changes the color to red for the traffic lights belonging to indexRed and to green
        /// for the traffic lights belonging to indexGreen.
        /// </summary>
        /// <param name="indexGreen">The index in the LinkedList where the traffic lights have to be green</param>
        /// <param name="indexRed">The index in the LinkedList where the traffic lights have to be red</param>
        private void ChangeColors(int indexGreen, int indexRed)
        {
            intervalToRun = myTrafficLight[indexGreen].ElementAt(0).intervalTobeGreen;

            foreach (TrafficLight t in myTrafficLight[indexRed])
            {
                t.color = Color.Red;
            }

            foreach (Lane l in myIncomingLanes[indexRed])
            {
                l.canMove = false;
            }

            if (indexRed == 2)
            {
                pedestrianLanes[1].ElementAt(0).canMove = false;
                pedestrianLanes[1].ElementAt(3).canMove = false;

                //for (int i = 0; i < myTrafficLight[5].Count; i++)
                //    myTrafficLight[5].ElementAt(i).color = Color.Red;

            }

            else if (indexRed == 3)
            {
                pedestrianLanes[0].ElementAt(0).canMove = false;
                pedestrianLanes[0].ElementAt(3).canMove = false;

                //for (int i = 0; i < myTrafficLight[4].Count; i++)
                //    myTrafficLight[4].ElementAt(i).color = Color.Red;
            }

            foreach (TrafficLight t in myTrafficLight[indexGreen])
            {
                t.color = Color.Green;
                intervalToRun = t.intervalTobeGreen;
            }

            foreach (Lane l in myIncomingLanes[indexGreen])
            {
                l.canMove = true;
            }

            if (indexGreen == 3)
            {
                pedestrianLanes[0].ElementAt(0).canMove = true;
                pedestrianLanes[0].ElementAt(3).canMove = true;

                //for (int i = 0; i < myTrafficLight[5].Count; i++)
                //    myTrafficLight[5].ElementAt(i).color = Color.Green;
            }
            else if (indexGreen == 2)
            {
                pedestrianLanes[1].ElementAt(0).canMove = true;
                pedestrianLanes[1].ElementAt(3).canMove = true;

                //for (int i = 0; i < myTrafficLight[4].Count; i++)
                //    myTrafficLight[4].ElementAt(i).color = Color.Green;
            }
          

            changed2orange = false;
        }

        /// <summary>
        ///  Is called before the traffic light expires and changes the traffic light to orange
        ///  also disables the cars from moving beyond the lanes boundaries
        /// </summary>
        /// <param name="indexGreen">the current Green indexer</param>
        private void changeToOrange(int indexGreen)
        {
            changed2orange = true;
            foreach (TrafficLight t in myTrafficLight[indexGreen])
            {
                if (t.GetType() == typeof(TLPedestrian))
                {
                    t.color = Color.Red;
                }
                else
                {
                    t.color = Color.Orange;
                }
            }
            //foreach (PedLane l in pedestrianLanes[1])
            //{
            //    l.canMove = false;
            //}

            //foreach (PedLane l in pedestrianLanes[0])
            //{
            //    l.canMove = false;
            //}

            foreach (Lane l in myIncomingLanes[indexGreen])
            {
                l.canMove = false;
            }

            for (int i=0; i<blackboxLanes.Count; i++)
            {
                for (int c=0; c < blackboxLanes[i].NumberOfCars; c++)
                        blackboxLanes[i].GetCarList()[c].speed = 5;
            }
        }

        /// <summary>
        /// If a pedLane has the sensor on, the sensor will call this function to turn all the lights red
        /// and also enable the pedLanes  of the sensor side to move
        /// </summary>
        /// <param name="pedLanesOuter">Defines wether is the north pedLanes or the South pedlanes that enabled the sensor</param>
        /// <param name="lanesIndx">Ped lanes index to enable moving</param>
        public void changeColorsBySensor(int tLightsIndx, int lanesIndx)
        {
            if (indexGreen == tLightsIndx)
                return;
            if (intervalToRun > 2000)
                return;

            // Known Traffic Ligths for the sensor at index 4 and index 5
            changed2orange = true;
            indexGreen = tLightsIndx;
            indexRed = (tLightsIndx - 1) % 4;

            

            intervalToRun = myTrafficLight[tLightsIndx].ElementAt(0).intervalTobeGreen;
            intervalRunning = 0;

            for (int i = 0; i < myTrafficLight.Length; i++ )
                foreach (TrafficLight t in myTrafficLight[i])
                {
                    t.color = Color.Red;
                }

          //  for (int i = 0; i < myIncomingLanes.Length; i++)
                foreach (Lane l in myIncomingLanes[indexRed])
                {
                    l.canMove = false;
                }
                ChangeColors(tLightsIndx, indexRed);
            //for (int i = 0; i < myIncomingLanes.Length; i++ )
            //    foreach (Lane l in myIncomingLanes[i])
            //    {
            //        l.canMove = false;
            //    }

            //for (int i = 0; i < myTrafficLight.Length - 1; i++ )
            //    foreach (TrafficLight t in myTrafficLight[i])
            //    {
            //        t.color = Color.Red;
            //    }

            //foreach (TrafficLight t in myTrafficLight[tLightsIndx])
            //    t.color = Color.Green;

            //for (int i = 0; i < pedestrianLanes[lanesIndx].Count; i++)
            //    pedestrianLanes[lanesIndx].ElementAt(i).canMove = true;    


            
        }

        /// <summary>
        /// This stops the crossing from running its pattern.
        /// </summary>
        public override void StopRunning()
        {
            timer.Stop();
            this.pbox.Invalidate();
        }


        /// <summary>
        /// This function resets the value of all the objects.
        /// </summary>
        public override void ResetObjects()
        {
            this.intervalRunning = 0;

            for (int i = 0; i < 4; i++)
            {
                foreach (Lane il in myIncomingLanes[i])
                {
                    il.ResetLane();
                }
            }
            foreach (Lane bl in blackboxLanes)
            {
                bl.ResetLane();
            }

            foreach (Lane ol in outgoingLanes)
            {
                ol.ResetLane();
            }

            for (int i = 0; i < 2; i++)
            {
                foreach (PedLane pl in pedestrianLanes[i])
                {
                    pl.ResetLane();
                }
            }

            for (int i =0; i < 4; i++)
            {
                foreach (TrafficLight tl in myTrafficLight[i])
                {
                    tl.color = Color.Red;
                }
            }            
            
            this.pbox.Invalidate();
        }


        /// <summary>
        /// This function generates a car on one of the lanes. First picks a departure, then from that
        /// departure picks a destination. Finally, adds that car to the lane.
        /// </summary>
        public override void GenerateCars(/*string depart*/)
        {
            Lane departure;
            //if(string == null)
            //{
             departure= this.PickDeparture();
             if (departure == null)
                 return;
            //}
            //else
            //{
            //    departure = getLane(depart); 
            //}
            Lane destination = departure.PickDestination();
            departure.AddCarLane(new Car(departure, destination, moveToBlackbox, null));
            /* Inside a lane, we have to have the list of possible destinations and a function 
             * to randomly pick a destination from the available ones.
             * My idea for a car is: with the departure and destination we can generate the starting 
             * position, inside the Car class we should generate the color and the speed.
             * */
        }


        /// <summary>
        /// This function randomly picks the departure of a car.
        /// </summary>
        /// <returns>It returns the lane of departure.</returns>
        public override Lane PickDeparture()
        {
            bool found = false;
            int indexList = 0;
            int indexLane = 0;
            int count = 0;
            while (!found && count < 6)
            {
                indexList = rdm.Next(4);
                indexLane = rdm.Next(myIncomingLanes[indexList].Count);
                if (myIncomingLanes[indexList].ElementAt(indexLane).isFull() || myIncomingLanes[indexList].ElementAt(indexLane).HasNeighbours())
                {
                    found = false;
                }
                else
                {
                    found = true;
                }
                count++;
            }
            return found ? myIncomingLanes[indexList].ElementAt(indexLane) : null;
                               
        }


        Random rdmPed = new Random();
        public void addPedestrian()
        {
            int numberToGenerate = rdmPed.Next(0, 4);

            for (int i = 0; i < numberToGenerate; i++)
            {
                PedLane departure;
                int indexList = rdmPed.Next(0, 2);
                int indexLane = rdmPed.Next(0, 2);
                int[] startPoint = { 0, 3 };
                departure = pedestrianLanes[indexList].ElementAt(startPoint[indexLane]);
                departure.addPedestrian(new Pedestrian(departure, departure.destination, MoveToPedLane, changeColorsBySensor));
       
            }
       }

       
        /// <summary>
        /// For every lane it gets the array of the positions of the cars.
        /// </summary>
        /// <returns>Returns an array containing all the points of the cars.</returns>
         public override void getallCars2Draw(object o,PaintEventArgs e)
        {             
             for (int j = 0; j < myTrafficLight.Length; j++)
            {
                foreach (TrafficLight tl in myTrafficLight[j])
                {
                    if (tl.color == Color.Green)
                        e.Graphics.FillEllipse(Brushes.Green, tl.position.X, tl.position.Y, 10, 10);
                    else if (tl.color == Color.Red)
                        e.Graphics.FillEllipse(Brushes.Red, tl.position.X, tl.position.Y, 10, 10);
                    else
                        e.Graphics.FillEllipse(Brushes.Orange, tl.position.X, tl.position.Y, 10, 10);
                }
            }

            
            for (int j = 0; j < myIncomingLanes.Length; j++)
            {
                foreach (Lane l in myIncomingLanes[j])
                {
                    for (int i = 0; i < l.NumberOfCars; i++)
                        e.Graphics.DrawImage(l.img, l.GetCarfromList(i).position);
                }
            }


            // Get the car Points from the BlackBoxLanes
            foreach (Lane l in blackboxLanes)
            {
                for (int i = 0; i < l.NumberOfCars; i++)
                    e.Graphics.DrawImage(l.img, l.GetCarfromList(i).position);
            }

            // Get the car Points from the outgoingLanes
            foreach (Lane l in outgoingLanes)
            {
                for (int i = 0; i < l.NumberOfCars; i++)
                    e.Graphics.DrawImage(l.img, l.GetCarfromList(i).position);
            }

             
                 for(int i=0; i< pedestrianLanes.Length; i++ )
                 {
                     foreach (PedLane l in pedestrianLanes[i])
                     {
                         for (int j = 0; j < l.pedNum; j++)
                             e.Graphics.DrawImage(l.img, l.GetPedestrianfromList(j).pos.X, l.GetPedestrianfromList(j).pos.Y,15,15);
                     }
                 }
        }


        /// <summary>
        /// It takes the moving object and removes it from the blackbox lane, adding to the outgoing lane.
        /// </summary>
        /// <param name="mo">The moving object to be moved.</param>
        public override void moveToLane(MovingObject mo)
        {
            Lane newLane = mo.desti;
            Lane previousLane = mo.depart;
            Lane newDestination = newLane.PickDestination();

            if (!newLane.isFull())
            {
                previousLane.RemoveCarLaneStart();
                newLane.AddCarLane(new Car(newLane, newDestination, null, changeToCrossing));
            }
            else
                return;
        }

        /// <summary>
        /// It takes the moving object and removes it from the this crossing, adding to the neighbour.
        /// </summary>
        /// <param name="mo">The moving object to be moved.</param>
        public override void changeToCrossing(MovingObject mo)
        {
            // if the lane in the other crossing is full, do nothing.
            // if there is no crossing connected, delete the Car.
            // If lane exist and is not full...
            // Calls the add car function of the other Crossing, It doesn't pass the car..

            if (mo.desti != null)
            {
                Lane previousLane = mo.depart;
                Lane newLane = mo.desti;
                Lane newDestination = newLane.PickDestination();
                if (!newLane.isFull())
                {
                    previousLane.RemoveCarLaneStart();
                    newLane.AddCarLane(new Car(newLane, newDestination, moveToBlackbox, null));
                }
                else
                    return;
            }
            else 
            {
                mo.depart.RemoveCarLaneStart();
            }
        }

        /// <summary>
        /// It takes the moving object and removes it from the ingoing lane, adding to the blackbox lane.
        /// </summary>
        /// <param name="mo"></param>
        public override void moveToBlackbox(MovingObject mo)
        {
            ///If the light is green and the lane to move isn't full
            ///change lane. otherwise do nothing
            Lane newLane = mo.desti;
            Lane previousLane = mo.depart;
            Lane newDestination = newLane.PickDestination();

            if (!newLane.isFull())
            {
                previousLane.RemoveCarLaneStart();
                newLane.AddCarLane(new Car(newLane, newDestination, moveToLane, null));
                int index = newLane.GetCarList().Count - 1;
                newLane.GetCarList()[index].isOnBlckbx = true;
                newLane.GetCarList()[index].speed = 3;// changes to true, it helps with the move functionality
            }
            else
                return;
        }

        /// <summary>
        /// Adds a neighbour at the East OR South side of the crossing
        /// </summary>
        /// <param name="c">A reference to the neighbouring crossing on the East or South side</param>
        public override void AddNeighbour(ref Crossing c)
        {
            if (c.getIndex() == this.index + 1) // on the right side
            {
                // Direction: West TO EAST
                // E3 Lane gets a west side neighbour
                List<Lane> psbl = c.getPossibleLanes(Direction.E);

                for (int i = 0; i < psbl.Count; i++)
                {
                    // Tell the incoming lane that a neighbour is present
                    psbl[i].addNeighbour(null);
                    this.outgoingLanes[getIndexLane("E3")].addNeighbour(psbl[i]);
                }
            }
            else if (c.getIndex() == this.index + 3) //  underneath you
            {
                // Direction: South TO North
                // S1 Lane gets a north side neigbour
                List<Lane> psbl = c.getPossibleLanes(Direction.S);
                int j = getIndexLane("S1");
                for (int i = 0; i < psbl.Count; i++)
                {
                    // Tell the incoming lane that a neighbour is present
                    psbl[i].addNeighbour(null);
                    this.outgoingLanes[getIndexLane("S1")].addNeighbour(psbl[i]);
                }
            }
            else
                return;
            
            // Tell to your neighbour to add you
            c.GetNeighbour((Crossing)this);
        }

        /// <summary>
        /// Sets the neigbhour for the East OR south side of the crossing
        /// </summary>
        /// <param name="c">The parent crossing on the West or North side of the crossing</param>
        public override void GetNeighbour(Crossing c)
        {

            if (c.getIndex() == this.index - 1) // on the left side
            {
                // Direction East TO West
                // W1 gets an East side neighbour
                List<Lane> psbl = c.getPossibleLanes(Direction.W);
                int j = getIndexLane("W1");
                for (int i = 0; i < psbl.Count; i++)
                {
                    psbl[i].addNeighbour(null);
                    this.outgoingLanes[j].addNeighbour(psbl[i]);
                }
            }
            else if (c.getIndex() == this.index - 3) // above you
            {
                // Direction North TO South
                // N3 gets a South Neighbour
                List<Lane> psbl = c.getPossibleLanes(Direction.N);

                for (int i = 0; i < psbl.Count; i++)
                {
                    psbl[i].addNeighbour(null);
                    this.outgoingLanes[getIndexLane("N3")].addNeighbour(psbl[i]);
                }
            }
            else
                return;
        }

        /// <summary>
        /// Finds all the incoming lanes of a given side.
        /// </summary>
        /// <param name="dir">Accepts the direction of the outgoing lane</param>
        /// <returns>Returns a list of incoming Lanes as possible destinations for the outgoing lane.</returns>
        public override List<Lane> getPossibleLanes(Direction dir)
        {
            List<Lane> toReturn = new List<Lane>();
            switch (dir)
            {
                case Direction.N:
                    toReturn = this.myIncomingLanes[1].ToList();
                    break;
                case Direction.E:
                    toReturn = this.myIncomingLanes[2].ToList();
                    break;
                case Direction.S:
                    toReturn = this.myIncomingLanes[0].ToList();
                    break;
                case Direction.W:
                    toReturn = this.myIncomingLanes[3].ToList();
                    break;
            }
            return toReturn;
        }

        public List<Lane> getLaneStatistics()
        {
            List<Lane> AllLanes = new List<Lane>();
            for (int i = 0; i < myIncomingLanes.Length; i++ )
                foreach (Lane l in myIncomingLanes[i])
                {
                    AllLanes.Add(l);
                }
            foreach (Lane l in blackboxLanes)
            { AllLanes.Add(l); }

                return AllLanes;
        }

        internal int getNumberOfPedestrian()
        {
            return this.myPedestrians.Count;
        }

        public override List<Pedestrian> getPedestrians()
        {
            List<Pedestrian> pd = new List<Pedestrian>();

            for(int i=0; i < pedestrianLanes.Length; i++)
            {
                for (int j = 0; j < pedestrianLanes[i].Count; j++)
                {
                    foreach (Pedestrian p in pedestrianLanes[i].ElementAt(j).getPedestrians())
                    {
                        pd.Add(p);
                    }
                }
            }
            return pd;
        }

        public override List<PedLane> getPedestrianLanes()
        {
            List<PedLane> pdLn = new List<PedLane>();

            for (int i = 0; i < pedestrianLanes.Length; i++)
            {
                for (int j = 0; j < pedestrianLanes[i].Count; j++)
                {
                    if (j== 0 || j==3)
                    pdLn.Add(pedestrianLanes[i].ElementAt(j));
                }
            }
            return pdLn;
        }

        public void MoveToPedLane(MovingObject pedo)
        {
            Pedestrian p = (Pedestrian)pedo;
            PedLane dep = (PedLane)p.departure;
            PedLane newLane = (PedLane)p.destination;
            
            if (p.destination == null)
            {
                dep.removePedestrian();

                //if (dep.getPedestrians().Count == 0 && myTrafficLight[4].ElementAt(0).color == Color.Green)
                //{
                //    for (int i = 0; i < pedestrianLanes[0].Count; i++)
                //        pedestrianLanes[0].ElementAt(i).canMove = false;

                //    myTrafficLight[4].ElementAt(0).color = Color.Red;
                //    myTrafficLight[4].ElementAt(1).color = Color.Red;
                //}
                //else if(dep.getPedestrians().Count == 0 && myTrafficLight[5].ElementAt(0).color == Color.Green)
                //{
                //    for (int i = 0; i < pedestrianLanes[1].Count; i++)
                //        pedestrianLanes[1].ElementAt(i).canMove = false;

                //    myTrafficLight[5].ElementAt(0).color = Color.Red;
                //    myTrafficLight[5].ElementAt(1).color = Color.Red;
             //   }
            
                return;
            }

            newLane.addPedestrian(new Pedestrian(newLane, newLane.destination, MoveToPedLane, null));
            dep.removePedestrian();

        }
    }
}
